#include "ASilhouette.h"


ASilhouette::ASilhouette(std::vector<CGMesh*>& m, int index, CGPoint p, AVLTree<EdgePair>& t) : meshlist(m), tree(t) {
	//set mesh index
	mesh = index;
	//set point of view
	pov = p;

	//clear the graph
	graph.clear();
	copygraph.clear();

	//clear the maps
	vertexToNode.clear();
	nodeToVertex.clear();
	edgeToArc.clear();
	arcToEdge.clear();

	//unmark all element in the mesh
	vcg::tri::UnMarkAll( *meshlist[mesh] );
}


ASilhouette::~ASilhouette(void) {
	//undirected graph representing the silhouette
	graph.clear();

	//correspondence maps
	vertexToNode.clear();
	nodeToVertex.clear();
	edgeToArc.clear();
	arcToEdge.clear();

	//quality for nodes (deformation of mesh vertices)
	delete deformation;
	delete significant;
}

void ASilhouette::addVertex(int index) {

	//first check if the node already exists
	//if ( vertexToNode.find(index) != vertexToNode.end() ) return;

	if ( vcg::tri::IsMarked(*meshlist[mesh], &meshlist[mesh]->vert[index]) ) {
		return;
	}
	//add the vertex with specified index to the graph
	lemon::ListGraph::Node n = graph.addNode();
	//mark the correspondent vertex
	vcg::tri::Mark(*meshlist[mesh], &meshlist[mesh]->vert[index]);
	
	//set the correspondances
	vertexToNode.insert( std::pair<int, lemon::ListGraph::Node> (index, n) );
	nodeToVertex.insert( std::pair<lemon::ListGraph::Node, int> (n, index) );

	//add element to the copy
	//copygraph.addNode();
}

void ASilhouette::addEdge(int from, int to, int index ) {
	//check if the edge already exist (maybe useless)
	//if ( edgeToArc.find(index) != edgeToArc.end() ) return;
	//get the node corresponding to "from" and "to" vertices
	lemon::ListGraph::Node u = vertexToNode[from];
	lemon::ListGraph::Node v = vertexToNode[to];
	//add the edge to the graph
	lemon::ListGraph::Edge e = graph.addEdge(u, v);
	
	//set correpondances
	edgeToArc.insert( std::pair<int, lemon::ListGraph::Edge> (index, e) );
	arcToEdge.insert( std::pair<lemon::ListGraph::Edge, int> (e, index) );

	//add edge to the copy
	//u = copygraph.nodeFromId(graph.id(u));
	//v = copygraph.nodeFromId(graph.id(v));
	//copygraph.addEdge(u,v);
}

void ASilhouette::save(QString filename, QString options, bool m, QColor color) {

	//get OpenGL matrices (for projection)
	GLdouble modelview[16];		glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
	GLdouble projection[16];	glGetDoublev(GL_PROJECTION_MATRIX, projection);
	GLint viewport[4];			glGetIntegerv(GL_VIEWPORT, viewport);

	//projection on the canvas
	GLdouble p0[3], p1[3];
	GLdouble w0[3], w1[3];

	//draw something
	QPixmap canvas = QPixmap(viewport[2], viewport[3]); //size of the viewport
	canvas.fill( Qt::white );
	int w = canvas.width();
	int h = canvas.height();
	
	/***************************************/
	if ( m ) {
		QImage* snapshot = new QImage( viewport[2], viewport[3], QImage::Format_ARGB32);
		snapshot->fill(0);

		//read data from framebuffer
		uchar* red = (uchar*) calloc (snapshot->width() * snapshot->height(), sizeof(uchar));
		uchar* green = (uchar*) calloc (snapshot->width() * snapshot->height(), sizeof(uchar));
		uchar* blue = (uchar*) calloc (snapshot->width() * snapshot->height(), sizeof(uchar));
		glReadPixels( (GLint) 0, (GLint) 0, (GLint) snapshot->width(), (GLint) snapshot->height(), GL_RED, GL_UNSIGNED_BYTE, red );
		glReadPixels( (GLint) 0, (GLint) 0, (GLint) snapshot->width(), (GLint) snapshot->height(), GL_GREEN, GL_UNSIGNED_BYTE, green );
		glReadPixels( (GLint) 0, (GLint) 0, (GLint) snapshot->width(), (GLint) snapshot->height(), GL_BLUE, GL_UNSIGNED_BYTE, blue );

		//save data in QImage
		for(int x = 0; x < snapshot->width(); x++)   {      //col
		   for(int y = 0; y < snapshot->height(); y++)  {   //row
			   //save image
			   snapshot->setPixel(x, snapshot->height()-y-1, qRgb(red[y*snapshot->width()+x], green[y*snapshot->width()+x], blue[y*snapshot->width()+x]));
		   }
		}

		canvas = QPixmap::fromImage(*snapshot);
	}

	QPainter painter;
	painter.begin(&canvas);           // paint in picture

	painter.setBackground( QBrush(Qt::white) ); 
	/***********************************/


	std::vector<QColor> colors;

	if (options == QString("clustering") ) {
		colors.resize( clusters.size() );

		for ( int i = 0; i < colors.size(); i ++ ) {
			colors[i] = QColor( rand()%160, rand()%160, rand()%160, 255);
		}
	}

	//loop on the edges
	for ( lemon::ListGraph::EdgeIt e(graph); e != lemon::INVALID; ++e ) {
		//get the sources!
		lemon::ListGraph::Node u = graph.u(e);
		lemon::ListGraph::Node v = graph.v(e);

		//get the vertices!
		int v0 = nodeToVertex[u];
		int v1 = nodeToVertex[v];

		//get the projections!
		gluProject(meshlist[mesh]->vert[v0].P().X(), meshlist[mesh]->vert[v0].P().Y(), meshlist[mesh]->vert[v0].P().Z(), modelview, projection, viewport, &w0[0], &w0[1], &w0[2]);
		gluProject(meshlist[mesh]->vert[v1].P().X(), meshlist[mesh]->vert[v1].P().Y(), meshlist[mesh]->vert[v1].P().Z(), modelview, projection, viewport, &w1[0], &w1[1], &w1[2]);
		
		//GET THE TABLES!!

		//clustering: color the edges using colors of different clusters
		if ( options == QString("clustering") ) {
			//search the vertex
			if ( vertexToCluster.find(v0) == vertexToCluster.end() ) continue;
			if ( vertexToCluster.find(v1) == vertexToCluster.end() ) continue;
			//vertex found!
			int c0 = vertexToCluster[v0];
			int c1 = vertexToCluster[v1];

			//same cluster for both vertex: draw a single color line and cicles around the vertices
			if ( c0 == c1 ) {
				painter.setPen( colors[c0] );
				painter.setBrush( QBrush(colors[c0]) );
				painter.drawLine( floor(w0[0]),  h - floor(w0[1]), floor(w1[0]),  h - floor(w1[1]) );
				//painter.drawEllipse( floor(w0[0]),  h - floor(w0[1]), 2, 2 );
				//painter.drawEllipse( floor(w1[0]),  h - floor(w1[1]), 2, 2 );

				//draw a red circle around deformation vertex, and a yellow circle on the others
                                if ( (*significant)[u] == signficant ) {
					painter.setPen( Qt::red );
					painter.setBrush( QBrush( Qt::red ) );
					painter.drawEllipse(QPoint(floor(w0[0]), h - floor(w0[1])), 2, 2);
				}

                                if ( (*significant)[v] == signficant ) {
					painter.setPen( Qt::red );
					painter.setBrush( QBrush( Qt::red ) );
					painter.drawEllipse(QPoint(floor(w1[0]), h - floor(w1[1])), 2, 2);
				}
			} 
			else {
				//different cluster: get the midpoint of the segment
				GLdouble mid[2];
				mid[0] = (w0[0] + w1[0])/2.0;
				mid[1] = (w0[1] + w1[1])/2.0;

				//draw from w0 to mid using color c0
				painter.setPen( colors[c0] );
				painter.setBrush( QBrush(colors[c0]) );
				painter.drawLine( floor(w0[0]), h - floor(w0[1]), floor(mid[0]), h - floor(mid[1]) );
				//painter.drawEllipse( floor(w0[0]),  h - floor(w0[1]), 2, 2 );
				//draw from mid to w1 using color c1
				painter.setPen( colors[c1] );
				painter.setBrush( QBrush(colors[c1]) );
				painter.drawLine( floor(mid[0]), h - floor(mid[1]), floor(w1[0]), h - floor(w1[1]) );
				//painter.drawEllipse( floor(w1[0]),  h - floor(w1[1]), 2, 2 );

                                if ( (*significant)[u] == signficant ) {
					painter.setPen( Qt::red );
					painter.setBrush( QBrush(Qt::red) );
					painter.drawEllipse(QPoint(floor(w0[0]), h - floor(w0[1])), 2, 2);
				}

                                if ( (*significant)[v] == signficant ) {
					painter.setPen( Qt::red );
					painter.setBrush( QBrush(Qt::red) );
					painter.drawEllipse(QPoint(floor(w1[0]), h - floor(w1[1])), 2, 2);
				}
			}
		
			//don't draw anything else
			continue;
		}

		//draw the line!
		painter.setPen( color );
		painter.drawLine( floor(w0[0]),  h - floor(w0[1]), floor(w1[0]),  h - floor(w1[1]) );
		painter.drawLine( floor(w0[0])+1,  h - floor(w0[1]), floor(w1[0])+1,  h - floor(w1[1]) );
		painter.drawLine( floor(w0[0])-1,  h - floor(w0[1]), floor(w1[0])-1,  h - floor(w1[1]) );
		painter.drawLine( floor(w0[0]),  h - floor(w0[1])+1, floor(w1[0]),  h - floor(w1[1])+1 );
		painter.drawLine( floor(w0[0]),  h - floor(w0[1])-1, floor(w1[0]),  h - floor(w1[1])-1 );

		//DEFORMATION: draw the significant vertex in red over the silhouette
		if ( options == QString("deformation") ) {
			//draw a red circle around deformation vertex, and a yellow circle on the others
                        if ( (*significant)[u] == signficant ) {
				painter.setBrush( QBrush(Qt::red) );
				painter.drawEllipse(QPoint(floor(w0[0]), h - floor(w0[1])), 4, 4);
			}

                        if ( (*significant)[v] == signficant ) {
				painter.setBrush( QBrush(Qt::red) );
				painter.drawEllipse(QPoint(floor(w1[0]), h - floor(w1[1])), 4, 4);
			}
		}
	
	}
	
	painter.end();              // painting done
	canvas.save(filename);		// save picture
}

void ASilhouette::finalize() {

	//set maps
	deformation = new lemon::ListGraph::NodeMap<double>(graph);
	significant = new lemon::ListGraph::NodeMap<VertexType>(graph);

	//init maps
	for ( lemon::ListGraph::NodeIt n(graph); n != lemon::INVALID; ++n ) {
		(*deformation)[n] = 0.0;	//no deformation
                (*significant)[n] = notsignificant;	//not significant
	}
}

CGPoint ASilhouette::getPov() { 
	return pov; 
}

lemon::ListGraph* ASilhouette::getGraph() {
	return &graph;	
}

std::vector<EdgePair> ASilhouette::getEdgePairs(lemon::ListGraph::Node n) {
	
	std::vector<EdgePair> output;

	//double loop to create the edge pair
	for ( lemon::ListGraph::IncEdgeIt ie0(graph, n); ie0 != lemon::INVALID; ++ie0 ) {
		for ( lemon::ListGraph::IncEdgeIt ie1(graph, n); ie1 != lemon::INVALID; ++ie1 ) {
			//check the id in order to avoid duplicates
			if ( graph.id( (lemon::ListGraph::Edge) ie0 ) <= graph.id( (lemon::ListGraph::Edge) ie1 ) ) continue;
			//create the index of edges and the node
			int e0 = arcToEdge[ie0];
			int e1 = arcToEdge[ie1];
			int v = nodeToVertex[n];
			EdgePair e(e0, e1, v);
			output.push_back(e);
		}
	}

	return output;
}

//set deformation value
void ASilhouette::setDeformationValue( lemon::ListGraph::Node n, double value ) {
	(*deformation)[n] = value;
}

void ASilhouette::vertexClustering(QString type, double ts) {

	//adaptive clustering (2-means)
	if ( type == QString("adaptive") ) {
		//initial cluster centroids
        double a = minquality;
        double b = maxquality;

		std::vector<Cluster> clusters;	//each cluster is represented by a vector of integers
		std::vector<double> centroids;	//centroids of cluster

                for ( int i = 0; i < numtypes; i ++ ) {
			Cluster c;
			clusters.push_back(c);
			centroids.push_back(0.0);
                        if ( i == signficant )		centroids[i] = maxquality ;
                        if ( i == notsignificant )	centroids[i] = minquality ;
		}
		//initially empty

		//init, all vertices not significant
		for ( lemon::ListGraph::NodeIt n(graph); n != lemon::INVALID; ++ n ) {
                        (*significant)[n] = notsignificant;
		}

		bool stop = false;
		while(!stop) {
			stop = true;
                        clusters[signficant].clear();
                        clusters[notsignificant].clear();

			//init, all vertices not significant
			for ( lemon::ListGraph::NodeIt n(graph); n != lemon::INVALID; ++ n ) {

				//get vertex index
                                if ( abs((*deformation)[n] - centroids[signficant] ) < abs((*deformation)[n] - centroids[notsignificant] ) ) {
                                        if ( (*significant)[n] != signficant ) stop = false;	//we need another loop
                                        (*significant)[n] = signficant;
                                        clusters[signficant].push_back(nodeToVertex[n]);
				}
				else {
                                        if ( (*significant)[n] != notsignificant ) stop = false;	//we need another loop
                                        (*significant)[n] =  notsignificant;
                                        clusters[notsignificant].push_back(nodeToVertex[n]);
				}
			}

			//recompute centroids
			std::vector<double> tmpcentroids(clusters.size());
			std::fill(tmpcentroids.begin(), tmpcentroids.end(), 0.0);
			for ( int i = 0; i < clusters.size(); i ++ ) {
				for ( int j = 0; j < clusters[i].size(); j ++ ) 
					tmpcentroids[i] += (*deformation)[vertexToNode[clusters[i][j]]];
				tmpcentroids[i] = tmpcentroids[i]/clusters[i].size();
				centroids[i] = tmpcentroids[i];
			}
		}
	}
	else {	//absolute thresholding
		//initial cluster centroids
        double a = minquality;
        double b = maxquality;

		std::vector<Cluster> clusters;	//each cluster is represented by a vector of integers
		std::vector<double> centroids;	//centroids of cluster

                for ( int i = 0; i < numtypes; i ++ ) {
			Cluster c;
			clusters.push_back(c);
			centroids.push_back(0.0);
                        if ( i == signficant )		centroids[i] = maxquality ;
                        if ( i == notsignificant )	centroids[i] = minquality ;
		}
		//initially empty

		//init, all vertices not significant
		for ( lemon::ListGraph::NodeIt n(graph); n != lemon::INVALID; ++ n ) {
                        (*significant)[n] = notsignificant;
		}

		for ( lemon::ListGraph::NodeIt n(graph); n != lemon::INVALID; ++ n ) {		
			if ( (*deformation)[n] > ts ) {	//significant!
                                (*significant)[n] = signficant;
                                clusters[signficant].push_back(nodeToVertex[n]);
			}
			else {	//not significant
                                (*significant)[n] =  notsignificant;
                                clusters[notsignificant].push_back(nodeToVertex[n]);
			}
		}
	}

	
}


double ASilhouette::analyzeEdgePair( EdgePair ep, QualityMeasure qual) {
	
	EdgePair* query = tree.find( ep );
	if ( query != NULL ) return query->getQual(); 

	static double a = 1.0;
	static double b = 0.3;
	static double c = 0.3;

	//index of vec0
	int p0, p1;
	//index of vec1
	int q0, q1;

	//p1 and q1 are the same vertex
	p1 = q1 = ep.getSharedVertex();

	//p0 and q0 take the other vertices
	p0 = graph.u(edgeToArc[ep.edges.first]) == vertexToNode[p1] ?  nodeToVertex[graph.v(edgeToArc[ep.edges.first])] : nodeToVertex[graph.u(edgeToArc[ep.edges.first])];
	q0 = graph.u(edgeToArc[ep.edges.second]) == vertexToNode[p1] ?  nodeToVertex[graph.v(edgeToArc[ep.edges.second])] : nodeToVertex[graph.u(edgeToArc[ep.edges.second])];
	
	//support vectors
 	std::vector< double > angles( meshlist.size() );
 	std::vector< double > length0( meshlist.size() );
 	std::vector< double > length1( meshlist.size() );

	vcg::Point3d vec0;
	vcg::Point3d vec1;

	for ( int i = 0; i < meshlist.size(); i ++ ) {
		//get vector
		vec0 = meshlist[i]->vert[p0].P() - meshlist[i]->vert[p1].P();
		vec1 = meshlist[i]->vert[q0].P() - meshlist[i]->vert[q1].P();

		if ( vec0.Norm() != 0.0 ) vec0.Normalize();
		if ( vec1.Norm() != 0.0 ) vec1.Normalize();
		//store values
		angles[i] = vcg::AngleN(vec0, vec1);		
		//angles[i] = vec0.dot(vec1); //alternatively
		length0[i] = (meshlist[i]->vert[p0].P() - meshlist[i]->vert[p1].P()).Norm();
		length1[i] = (meshlist[i]->vert[q0].P() - meshlist[i]->vert[q1].P()).Norm();
	}


        if ( qual == mean ) {
		
		double qual = 0.0;
		double qual0 = 0.0;
		double qual1 = 0.0;
		int m = 0;
		for ( int i = 0; i < angles.size(); i ++ ) {
			for ( int j = i+1; j < angles.size(); j ++ ) {
				qual += abs(angles[i]-angles[j]);
				qual0 += abs(length0[i]-length0[j]);
				qual1 += abs(length1[i]-length1[j]);
				m++;
			}
		}

		ep.setQual((a*qual + b*qual0 + c*qual1)/m);
		tree.insert(ep);

		return (a*qual + b*qual0 + c*qual1)/m;
	}


        if ( qual == maxq ) {

		double mina = angles[0];
		double maxa = angles[0]; 
		double minl0 = length0[0];
		double maxl0 = length0[0];
		double minl1 = length1[0];
		double maxl1 = length1[0];

		for ( int i = 1; i < angles.size(); i ++ ) {
			//search for minmax
			if ( angles[i] < mina ) mina = angles[i];
			if ( angles[i] > maxa ) maxa = angles[i];
			
			if ( length0[i] < minl0 ) minl0 = length0[i];
			if ( length0[i] > maxl0 ) maxl0 = length0[i];
			
			if ( length1[i] < minl1 ) minl1 = length1[i];
			if ( length1[i] > maxl1 ) maxl1 = length1[i];
		}

		//get quality
		double qual = abs(maxa - mina);
		double qual0 = abs(maxl0 - minl0);
		double qual1 = abs(maxl1 - minl1);

		ep.setQual(a*qual + b*qual0 + c*qual1);
		tree.insert(ep);

		return (a*qual + b*qual0 + c*qual1);
	}

	//actually we use the std deviation
        if ( qual == variance ) {
		double mean = 0.0;
		double meansquare = 0.0;

		double mean0 = 0.0;
		double meansquare0 = 0.0;

		double mean1 = 0.0;
		double meansquare1 = 0.0;

		for ( int i = 0; i < angles.size(); i ++ ) {
			mean += abs( angles[i] );
			meansquare += ( angles[i]*angles[i] );

			mean0 += abs( length0[i] );
			meansquare0 += ( length0[i] * length0[i] );

			mean1 += abs( length1[i] );
			meansquare1 += ( length1[i] * length1[i] );
		}

		double val = (meansquare/(angles.size()) - (mean/(angles.size()))*(mean/(angles.size())));
		double val0 = (meansquare0/(length0.size()) - (mean0/(length0.size()))*(mean0/(length0.size())));
		double val1 = (meansquare1/(length1.size()) - (mean1/(length1.size()))*(mean1/(length1.size())));

		ep.setQual(a*sqrt(val) + b*sqrt(val0) + c*sqrt(val1));
		tree.insert(ep);
		return a*sqrt(val) + b*sqrt(val0) + c*sqrt(val1);
	}

	return 0.0;
}


//segmentation
void ASilhouette::computeSaliency(QString clusteringType, QualityMeasure qual, Eigen::SparseVector<int>& saliency) {

    minquality = 1000;
    maxquality = -1000;
	//loop on nodes
	for ( lemon::ListGraph::NodeIt n(graph); n != lemon::INVALID; ++n ) {
		std::vector<EdgePair> ep =  getEdgePairs(n);
		double def = 0.0;
		//compute deformation
		for ( int i = 0; i < ep.size(); i ++ ) def = max(def, analyzeEdgePair( ep[i], qual ) );
		//store deformation in silhouette
		setDeformationValue(n, def);
        minquality = min(minquality, def);
        maxquality = max(maxquality, def);
	}
	
	//separate between significant and not-significant vertices
	vertexClustering(clusteringType, 0.2);

	//classifies vertices
	for ( lemon::ListGraph::NodeIt n(graph); n != lemon::INVALID; ++n ) {
                if ( (*significant)[n] == signficant )
			saliency.coeffRef( nodeToVertex[n] )++;
	}

	
}
